package de.mmis.core.base.abstracttree;

import java.util.Arrays;

import de.mmis.core.base.Toolbox;

/**
 * represents an abstract Tree that is an atomic byte sequence, possibly with
 * display hint
 * 
 * @author Christian Koch
 */
public class LeafNode extends Tree {
	/**
	 * enumeration of all possible encodings of atomic abstract Tree data
	 * 
	 * @author Christian Koch
	 */
	public enum Encoding {
		Token, Quoted, Hex, Verbatim, Base64
	}

	private byte[] data;
	private Encoding encoding;

	/**
	 * creates new Atom with empty data, Token encoding and no display hint
	 */
	public LeafNode() {
		this(new byte[0], Encoding.Token);
	}

	/**
	 * creates new Atom containing specified UTF-8 encoded string, Token
	 * encoding and no display hint
	 */
	public LeafNode(String UTF8data) {
		this(UTF8data, null);
	}

	/**
	 * creates new Atom containing specified UTF-8 encoded string and encoding,
	 * no display hint
	 */
	public LeafNode(String UTF8data, Encoding atomEncoding) {
		this.encoding = atomEncoding;
		this.data = Toolbox.toUTF8Bytes(UTF8data);

		if (this.encoding == null) // no encoding specified, auto-selecting
									// token
			this.encoding = Encoding.Token;
	}

	/**
	 * creates new Atom with given data and encoding
	 * 
	 * @param data
	 *            data of Atom to be created
	 * @param encoding
	 *            encoding of Atom to be created
	 */
	public LeafNode(byte[] data, Encoding encoding) {
		this.data = data;
		this.encoding = encoding;
	}

	/**
	 * sets data of this atom
	 * 
	 * @param data
	 *            new data
	 */
	public void setData(byte[] data) {
		this.data = data;
	}

	/**
	 * sets data of this atom to specified UTF-8 string
	 * 
	 * @param data
	 *            new data
	 */
	public void setUTF8Data(String data) {
		this.data = Toolbox.toUTF8Bytes(data);
	}

	/**
	 * @return data of this Atom
	 */
	public byte[] getData() {
		return this.data;
	}

	public String getUTF8Data() {
		return Toolbox.toUTF8String(this.data);
	}

	/**
	 * sets encoding type of this atom
	 * 
	 * @param encoding
	 *            new encoding type
	 */
	public void setEncoding(Encoding encoding) {
		this.encoding = encoding;
	}

	/**
	 * @return encoding type of this Atom
	 */
	public Encoding getEncoding() {
		return this.encoding;
	}

	/**
	 * @return type of this Tree instance, which is always Type.Atom for Atoms
	 */
	@Override
	public Type getType() {
		return Type.LEAF;
	}

	/**
	 * removes all base64 container abstract Tree wrappers from this abstract
	 * Tree
	 * 
	 * @param dataCopy
	 *            true, if Atom abstract Trees should also been cloned
	 * @return abstract Tree with same structure and content as current one and
	 *         not including any base64 container
	 */
	@Override
	public Tree copy(boolean dataCopy) {
		if (dataCopy)
			return new LeafNode(this.data == null ? null : this.data.clone(),
					this.encoding);
		return this;
	}

	/**
	 * checks if the content of this atom equals given UTF-8-String
	 * 
	 * @param s
	 *            UTF-8-String to check
	 * @return true, if both Strings are equal, else false
	 */
	public boolean equalsUTF8String(String s) {
		byte[] b = Toolbox.toUTF8Bytes(s);
		if (b.length != this.data.length)
			return false;
		for (int i = 0; i < b.length; i++)
			if (b[i] != this.data[i])
				return false;
		return true;
	}

	@Override
	public int hashCode() {
		int hash = Arrays.hashCode(this.data);
		hash += this.encoding.hashCode();
		return hash;
	}

	@Override
	protected boolean equals(Tree e, boolean wildcards) {
		if (wildcards && e.getType() == Type.LEAF
				&& ((LeafNode) e).getEncoding() == Encoding.Token
				&& ((LeafNode) e).equalsUTF8String("?"))
			return true;

		if (e.getType() != Type.LEAF)
			return false;

		if (this.getEncoding() != ((LeafNode) e).getEncoding())
			return false;

		if (this.getData().length != ((LeafNode) e).getData().length)
			return false;
		for (int i = 0; i < this.getData().length; i++)
			if (this.getData()[i] != ((LeafNode) e).getData()[i])
				return false;

		return true;
	}
}
